<?php

class DBU_Updater {
    protected $_db = false;
    protected $_dbBackup = false;
    
    protected $_versionTable = 'meta';
    protected $_versionField = 'versionDb';
    protected $_versionValue = false;
    protected $_versionValueDefault = '1';

    protected $_dbTables = array('db' =>false, 'files' => false);

    protected $_logs = array();


    public function runUpdate() {
        $this->updateTables();
        $this->runChangesets();
        $this->verbose('========================');
        $this->verbose('========================');
        $this->verbose('UPDATE COMPLETED');
    }


    public function installTables($tables) {
        foreach ($tables as $table) {
            if (!$this->tableDbExits($table)) {
                $this->installTable($table);
            }
        }
    }


    protected function installTable($table) {
        $this->verbose('<b> install table [' . $table . '] </b>');
        $this->runFile($this->tablePath($table)); 
        $this->runFile($this->dataPath($table));
    }


    public function structureTables($tables) {
        foreach ($tables as $table) {
            $this->structureTable($table);
        }
    }

    public function structureTable($table) {
        $this->getDbBackup()->saveTableStructure($table, $this->tablePath($table));
    }


    public function dataTables($tables) {
        foreach ($tables as $table) {
            $this->dataTable($table);
        }
    }

    public function dataTable($table) {
        $this->getDbBackup()->saveTableData($table, $this->dataPath($table));
    }

    public function runChangesets() {
        $this->verbose('========================');
        $this->verbose('<b> running Changes </b>');
        $changesets = $this->getChangesets();
        if (is_array($changesets) && count($changesets) > 0) {
            foreach ($changesets as $changeset) {
                $this->runChangeset($changeset);
            }

            $this->setVersion($changeset);
        }
    }

    public function getFileTables($reset = false) {
        if ( $this->_tables['files'] == false or $reset ) {
            $this->verbose('<b>getting Files tables</b>');
            $tables = $this->getFiles($this->tablePath());
            if (is_array($tables)) {
                foreach ($tables as $table) {
//                    $this->verbose('-- ' . $table);
                }
            }
            $this->_tables['files'] = $tables;

            
        } else {
            sort($this->_tables['files']);
        }
        return $this->_tables['files'];
    }

    public function getFileTablesData($reset = false) {
        if ( $this->_tables['data'] == false or $reset ) {
            $this->verbose('<b>getting Files Data tables</b>');
            $tables = $this->getFiles($this->dataPath());
            if (is_array($tables)) {
                foreach ($tables as $table) {
//                    $this->verbose('-- ' . $table);
                }
            }
            $this->_tables['data'] = $tables;
        }

        return $this->_tables['data'];
    }
    

    public function getChangesets($current = false) {
        $dirsTemp = $this->getDirs($this->changesetDirPath());
        $version = $this->getVersion();
        $dirs = array();

        $this->verbose('-- getting Changesets');
        if (is_array($dirs)) {
            foreach ($dirsTemp as $dir) {
                if (!$current OR ((int)$dir > (int)$current )) {
                    $dirPath = $this->changesetDirPath($dir);
                    $files = $this->getFiles($dirPath);
                    $dirs[$dir] = $files;
                }
            }
        }
        return $dirs;
    }

    public function getChangesetFiles($changeset) {
        $dirPath = $this->changesetDirPath($changeset);
        return $this->getFiles($dirPath);
    }

    public function runToChangeset($toChangeset) {
        $version = $this->getVersion();
        if ($toChangeset > $version) {
            $changesets = $this->getChangesets($version);
            foreach ($changesets as $changeset => $files) {
                if ((int) $changeset <= (int)$toChangeset) {
                    $this->runChangeset($changeset);
                }
            }
            $this->setVersion($toChangeset);
        }
    }

    public function runChangeset($changeset) {
        $this->verbose('-- runing changeset  [' . $changeset . ']');
        $files = $this->getChangesetFiles($changeset);
        if (is_array($files)) {
            foreach ($files as $file) {
                $this->verbose('---- file [' . $file . ']');
                $this->runFile($this->changesetPath($file, $changeset));
            }
        } else {
            $this->verbose('---- no files in [' . $dirPath . ']');
        }
        return false;
    }

    protected function getFiles($dir) {
        if (is_dir($dir)) {
            if ($dh = opendir($dir)) {
                while (($file = readdir($dh)) !== false) {
                    if ($file!='.' && $file!='..') {
                        $type = substr($file,(strrpos($file, ".")+1));
                        $name = substr($file,0,strrpos($file, "."));
                        if ($type == 'sql') {
                            $files[] = $name;
                        } // de la if ...
                    }
                }
                closedir($dh);
                if (is_array($files)) {
                    sort($files, SORT_NUMERIC);
                }
                return $files;
            }
        }

        return false;
    }

    protected function getDirs($dir) {
        if (is_dir($dir)) {
           if ($dh = opendir($dir)) {
               $dirs = array();
               while (($file = readdir($dh)) !== false) {
                  if ($file!='.' && $file!='..'  && $file!='.svn' && is_dir($dir . $file)) {
                      $dirs[] = $file;
                  }
               }
               closedir($dh);
               sort($dirs, SORT_NUMERIC);
               return $dirs;
           }
        }

        return false;
    }


    public function fileContent ($file, $changeset) {
        return file_get_contents($this->changesetPath($file, $changeset));
    }

    protected function runFile($filePath) {
        $this->verbose('---- runing file [' . $filePath . ']');
        if (is_file($filePath)) {
            $content = file_get_contents($filePath);
            if (!empty ($content)) {
                $queries = preg_split('/[.+;][\s]*\n/', $content, -1, PREG_SPLIT_NO_EMPTY);
                if (is_array($queries)) {
                    foreach ($queries as $query) {
                        $query = trim($query);
                        if (!empty ($query)) {
                            $this->runQuery($query);
                        }
                    }
                }
            }
        } else {
            $this->verbose('-- error no file [' . $filePath . ']');
        }

        return false;
    }

    public function getVersion($reset = false) {
        if (!$this->_versionValue OR $reset) {
            $this->verbose('<b> getting version from DB</b>');
            if (!$this->tableDbExits($this->_versionTable)) {
                $this->createVersionTable();
                $this->setVersion($this->_versionValueDefault);
            } else {
                $result = $this->getDb()->select('value')->from($this->_versionTable)->where(array('name', $this->_versionField))->go();
                if ($result) {
                    $row = $result->fetchResult();
                }
                if (is_numeric($row['value'])) {
                    $this->_versionValue = $row['value'];
                } else {
                    $this->insertVersionData();
                    $this->setVersion($this->_versionValueDefault);
                }
            }

            $this->verbose('-- Curent Version [' . $this->_versionValue . ']');
        }
        return intval($this->_versionValue);
    }

    protected function setVersion($version = false) {
        if (is_numeric($version)) {
            $this->_versionValue = $version;
            $this->updateVersion();
        } else {
            die('VERSION IS NOT NUMERIC');
        }
    }

    protected function updateVersion() {

        $this->verbose('<b> updating version table </b>');
        $sql = "UPDATE `{$this->_versionTable}` SET `value` = '{$this->_versionValue}' WHERE `name` = '{$this->_versionField}' LIMIT 1 ;";
        $this->runQuery($sql);

        $this->verbose('-- Updated version to [' . $this->_versionValue . ']');
    }


    protected function createVersionTable() {
        $this->verbose('<b> creating version table </b>');
        $sqlCreate = '
            CREATE TABLE `'.$this->_versionTable.'` (
            `id` int(11) NOT NULL auto_increment,
            `name` varchar(255) NOT NULL,
            `value` text NOT NULL,
            PRIMARY KEY  (`id`),
            UNIQUE KEY `name` (`name`)
            ) ENGINE=MyISAM  DEFAULT CHARSET=latin1';
        $this->runQuery($sqlCreate);
        $this->insertVersionData();
    }

    protected function insertVersionData() {
        $this->verbose('<b>inserting version table </b>');
        $sqlCreate = "INSERT INTO `{$this->_versionTable}` ( `name`, `value`) VALUES ( '{$this->_versionField}', '{$this->_versionValue}') ";
        $this->runQuery($sqlCreate);
    }

    protected function connectDb() {
        $this->_db = new DBU_Db_Wrapper(DB_ADAPTER);
        $this->_db->connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
    }


    /**
     * Returns the mysql object
     * @return DBU_Db_Wrapper
     */
    protected function getDb() {
        if (!is_object($this->_db)) {
            $this->connectDb();
        }
        return $this->_db;
    }


    /**
     * Returns the MySQLDump object
     * @return MySQLDump
     */
    protected function getDbBackup() {
        if (!is_object($this->_dbBackup)) {
            $this->_dbBackup = new DBU_Backup();
            $this->_dbBackup->setDb($this->getDb());
        }
        return $this->_dbBackup;
    }


    protected function runQuery($query) {
        $this->getDb()->execute($query);
    }

    public function getDbTables($reset = false) {
        if ( $this->_tables['db'] == false or $reset ) {
            $this->verbose('<b>getting DB tables</b>');
            $temp = $this->getDb()->listTables();
            $this->_tables['db'] = array();

            if (is_array($temp)) {
                foreach ($temp as $row) {
                    $table = array_shift($row);
//                    $this->verbose('-- ' . $table);
                    $this->_tables['db'][] = $table;
                }
            }
        }

        sort($this->_tables['db']);
        return $this->_tables['db'];
    }


    protected function tableDbExits($table) {
        return in_array($table, $this->getDbTables());
    }

    protected function tablePath($table = false) {
        $path = DB_TABLES_PATH;
        if (is_string($table)) {
            $path .= $table . '.sql';
        }

        return $path;
    }
    
    protected function dataPath($table = false) {
        $path = DB_DATA_PATH;
        if (is_string($table)) {
            $path .= $table . '.sql';
        }

        return $path;
    }

    public function changesetDirPath($changeset = false) {
        $path = DB_CHANGESETS_PATH;
        if (!empty ($changeset)) {
            $path .= $changeset . '/';
        }

        return $path;
    }

    public function changesetPath($file = false, $changeset = false) {
        $path = $this->changesetDirPath($changeset);
        $path .= $file . '.sql';
        return $path;
    }

    protected function verbose($message) {
        switch ($verbose) {
            default:
                DBU_Logger::instance()->add($message);
                break;
        }
    }

    public function getLog() {
        return $this->_logs;
    }
}